{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "keras.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.3"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CCQY7jpBfMur"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "z6X9omPnfO_h",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "F1xIRPtY0E1w"
      },
      "source": [
        "# Keras"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VyOjQZHhZxaA"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ja/r1/guide/keras.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ja/r1/guide/keras.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EhmkAPm44hhF",
        "colab_type": "text"
      },
      "source": [
        "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ViAXWoKlZ8s6"
      },
      "source": [
        "Kerasは、深層学習モデルを構築・学習するための高水準APIです。  \n",
        "迅速なプロトタイピングから先端研究、実運用にも使用されており、3つの特徴があります：\n",
        "\n",
        "- <b>ユーザーフレンドリー</b><br>\n",
        "  一般的なユースケースに最適化したKerasのAPIは、シンプルで統一性があります。誤った使い方をした場合のエラー出力も明快で、どう対応すべきか一目瞭然です。\n",
        "- <b>モジュール性</b><br>\n",
        "  Kerasのモデルは、設定可能なモジュールをつなぎ合わせて作られます。モジュールのつなぎ方には、ほとんど制約がありません。\n",
        "- <b>拡張性</b><br>\n",
        "  簡単にモジュールをカスタマイズできるため、研究の新しいアイデアを試すのに最適です。新しい層、損失関数を自作し、最高水準のモデルを開発しましょう。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "IsK5aF2xZ-40"
      },
      "source": [
        "## tf.keras のインポート\n",
        "\n",
        "`tf.keras` は、TensorFlow版 [Keras API 仕様](https://keras.io) です。 \n",
        "モデルを構築・学習するための高水準APIであり、TensorFlow特有の機能である\n",
        " [Eagerモード](#eager_execution)や`tf.data` パイプライン、 [Estimators](./estimators.md) にも正式に対応しています。\n",
        "`tf.keras` は、TensorFlowの柔軟性やパフォーマンスを損ねることなく使いやすさを向上しています。\n",
        "\n",
        "TensorFlowプログラムの準備として、先ずは `tf.keras` をインポートしましょう："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HOXBMnKbd0jL",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "!pip install tensorflow==\"1.*\""
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Qoc055N3wiUG",
        "colab": {}
      },
      "source": [
        "!pip install pyyaml  # YAML形式でモデルを保存する際に必要です。"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "TgPcBFru0E1z",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import tensorflow as tf\n",
        "from tensorflow.keras import layers\n",
        "\n",
        "print(tf.version.VERSION)\n",
        "print(tf.keras.__version__)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lj03RamP0E13"
      },
      "source": [
        "`tf.keras` ではKerasと互換性のあるコードを実行できますが、注意点もあります：\n",
        "\n",
        "* 最新リリースのTensorFlowに同梱されている `tf.keras` のバージョンと、pipインストールした最新の `keras` のバージョンが同一とは限りません。バージョンは `tf.keras.__version__` の出力をご確認ください。\n",
        "* [モデルの重みを保存](#weights_only)する場合、\n",
        "`tf.keras` のデフォルトの保存形式は  [チェックポイント形式](./checkpoints.md)です。\n",
        "HDF5形式にて保存する場合は、 `save_format='h5'` オプションを指定してください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "7e1LPcXx0gR6"
      },
      "source": [
        "## 単純なモデルの構築\n",
        "\n",
        "### シーケンシャル モデル\n",
        "\n",
        "Kerasでは、<b>層</b>を組み合わせて<b>モデル</b>を構築します。\n",
        "モデルは通常、複数の層から成るグラフ構造をしています。\n",
        "最も一般的なモデルは、単純に層を積み重ねる類の `tf.keras.Sequential` モデルです。\n",
        "\n",
        "単純な全結合ネットワーク（いわゆる マルチ レイヤー パーセプトロン）を構築してみましょう："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "WM-DUVQB0E14",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential()\n",
        "# ユニット数が64の全結合層をモデルに追加します：\n",
        "model.add(layers.Dense(64, activation='relu'))\n",
        "# 全結合層をもう一つ追加します：\n",
        "model.add(layers.Dense(64, activation='relu'))\n",
        "# 出力ユニット数が10のソフトマックス層を追加します：\n",
        "model.add(layers.Dense(10, activation='softmax'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "-ztyTipu0E18"
      },
      "source": [
        "### 層の設定\n",
        "\n",
        "`tf.keras.layers` はさまざまな層を提供していますが、共通のコンストラクタ引数があります：\n",
        "\n",
        "* `activation`： 層の活性化関数を設定します。組み込み関数、もしくは呼び出し可能オブジェクトの名前で指定します。デフォルト値は、活性化関数なし。\n",
        "* `kernel_initializer` ・ `bias_initializer`： 層の重み（カーネルとバイアス）の初期化方式。名前、もしくは呼び出し可能オブジェクトで指定します。デフォルト値は、 `\"Glorot uniform\"` 。\n",
        "* `kernel_regularizer` ・ `bias_regularizer`：層の重み（カーネルとバイアス）に適用する、L1やL2等の正則化方式。デフォルト値は、正則化なし。\n",
        "\n",
        "コンストラクタ引数を使って `tf.keras.layers.Dense` 層をインスタンス化する例を以下に示します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "MlL7PBtp0E19",
        "colab": {}
      },
      "source": [
        "# シグモイド層を１層作る場合：\n",
        "layers.Dense(64, activation='sigmoid')\n",
        "# 別の記法：\n",
        "layers.Dense(64, activation=tf.sigmoid)\n",
        "\n",
        "# カーネル行列に係数0,01のL1正則化を課した全結合層：\n",
        "layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))\n",
        "\n",
        "# バイアスベクトルに係数0,01のL2正則化を課した全結合層：\n",
        "layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))\n",
        "\n",
        "# カーネルをランダム直交行列で初期化した全結合層：\n",
        "layers.Dense(64, kernel_initializer='orthogonal')\n",
        "\n",
        "# バイアスベクトルを2.0で初期化した全結合層：\n",
        "layers.Dense(64, bias_initializer=tf.keras.initializers.constant(2.0))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "9NR6reyk0E2A"
      },
      "source": [
        "## 学習と評価\n",
        "\n",
        "### 学習の準備\n",
        "\n",
        "モデルを構築したあとは、`compile` メソッドを呼んで学習方法を構成します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sJ4AOn090E2A",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([\n",
        "# ユニット数64の全結合層をモデルに追加する：\n",
        "layers.Dense(64, activation='relu', input_shape=(32,)),\n",
        "# もう１層追加する：\n",
        "layers.Dense(64, activation='relu'),\n",
        "# 出力ユニット数10のソフトマックス層を追加する：\n",
        "layers.Dense(10, activation='softmax')])\n",
        "\n",
        "model.compile(optimizer=tf.train.AdamOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "HG-RAa9F0E2D"
      },
      "source": [
        "`tf.keras.Model.compile` には3つの重要な引数があります：\n",
        "\n",
        "* `optimizer`: このオブジェクトが訓練方式を規定します。 `tf.train` モジュールから\n",
        "  `tf.train.AdamOptimizer`や `tf.train.RMSPropOptimizer`、\n",
        "  `tf.train.GradientDescentOptimizer`等のオプティマイザ インスタンスを指定します。\n",
        "* `loss`: 最適化の過程で最小化する関数を指定します。平均二乗誤差（`mse`）や`categorical_crossentropy`、\n",
        "  `binary_crossentropy`等が好んで使われます。損失関数は名前、もしくは `tf.keras.losses` モジュールから呼び出し可能オブジェクトとして指定できます。\n",
        "* `metrics`: 学習の監視に使用します。 名前、もしくは`tf.keras.metrics` モジュールから呼び出し可能オブジェクトとして指定できます。\n",
        "\n",
        "学習用モデルの構成例を2つ、以下に示します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "St4Mgdar0E2E",
        "colab": {}
      },
      "source": [
        "# 平均二乗誤差 回帰モデルを構成する。\n",
        "model.compile(optimizer=tf.train.AdamOptimizer(0.01),\n",
        "              loss='mse',       # 平均二乗誤差\n",
        "              metrics=['mae'])  # 平均絶対誤差\n",
        "\n",
        "# 多クラス分類モデルを構成する。\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.01),\n",
        "              loss=tf.keras.losses.categorical_crossentropy,\n",
        "              metrics=[tf.keras.metrics.categorical_accuracy])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "yjI5rbi80E2G"
      },
      "source": [
        "### NumPy データの入力\n",
        "\n",
        "小規模なデータセットであれば、モデルを学習・評価する際にインメモリの [NumPy](https://www.numpy.org/)配列を使いましょう。\n",
        "モデルは `fit` メソッドを使って学習データに適合させます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "3CvP6L-m0E2I",
        "colab": {}
      },
      "source": [
        "import numpy as np\n",
        "\n",
        "def random_one_hot_labels(shape):\n",
        "  n, n_class = shape\n",
        "  classes = np.random.randint(0, n_class, n)\n",
        "  labels = np.zeros((n, n_class))\n",
        "  labels[np.arange(n), classes] = 1\n",
        "  return labels\n",
        "\n",
        "data = np.random.random((1000, 32))\n",
        "labels = random_one_hot_labels((1000, 10))\n",
        "\n",
        "model.fit(data, labels, epochs=10, batch_size=32)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "N-pnVaFe0E2N"
      },
      "source": [
        "`tf.keras.Model.fit` は3つの重要な引数があります：\n",
        "\n",
        "* `epochs`:  **エポック** は学習の構成単位で、（バッチに分割した）全入力データを一巡したものを1エポックと換算します。\n",
        "* `batch_size`: NumPyデータを渡されたモデルは、データをバッチに分割し、それを順繰りに舐めて学習を行います。一つのバッチに配分するサンプル数を、バッチサイズとして整数で指定します。全サンプル数がバッチサイズで割り切れない場合、最後のバッチだけ小さくなる可能性があることに注意しましょう。\n",
        "* `validation_data`: モデルの試作中に評価データを使って簡単にパフォーマンスを監視したい場合は、この引数に入力とラベルの対を渡すことで、各エポックの最後に推論モードで評価データの損失と評価指標を表示することができます。\n",
        "\n",
        "`validation_data` の使用例："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "gFcXzVQa0E2N",
        "colab": {}
      },
      "source": [
        "import numpy as np\n",
        "\n",
        "data = np.random.random((1000, 32))\n",
        "labels = random_one_hot_labels((1000, 10))\n",
        "\n",
        "val_data = np.random.random((100, 32))\n",
        "val_labels = random_one_hot_labels((100, 10))\n",
        "\n",
        "model.fit(data, labels, epochs=10, batch_size=32,\n",
        "          validation_data=(val_data, val_labels))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "-6ImyXzz0E2Q"
      },
      "source": [
        "### tf.data データセットの入力\n",
        "\n",
        "大規模なデータセット、もしくは複数デバイスを用いた学習を行う際は [Datasets API](./datasets.md) を使いましょう。 `fit`メソッドに`tf.data.Dataset` インスタンスを渡します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "OziqhpIj0E2R",
        "colab": {}
      },
      "source": [
        "# データセットのインスタンス化の例：\n",
        "dataset = tf.data.Dataset.from_tensor_slices((data, labels))\n",
        "dataset = dataset.batch(32)\n",
        "dataset = dataset.repeat()\n",
        "\n",
        "# `fit` にデータセットを渡す際は、`steps_per_epoch` の指定をお忘れなく：\n",
        "model.fit(dataset, epochs=10, steps_per_epoch=30)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "I7BcMHkB0E2U"
      },
      "source": [
        "`fit` メソッドの引数 `steps_per_epoch` には、1エポックあたりの学習ステップ数を指定します。\n",
        "`Dataset` がバッチを生成するため `batch_size`の指定は不要です。\n",
        "\n",
        "`Dataset` は評価データにも使えます："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "YPMb3A0N0E2V",
        "colab": {}
      },
      "source": [
        "dataset = tf.data.Dataset.from_tensor_slices((data, labels))\n",
        "dataset = dataset.batch(32).repeat()\n",
        "\n",
        "val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))\n",
        "val_dataset = val_dataset.batch(32).repeat()\n",
        "\n",
        "model.fit(dataset, epochs=10, steps_per_epoch=30,\n",
        "          validation_data=val_dataset,\n",
        "          validation_steps=3)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "IgGdlXso0E2X"
      },
      "source": [
        "### 評価と推論\n",
        "\n",
        "`tf.keras.Model.evaluate` と `tf.keras.Model.predict` メソッドは、NumPyデータと`tf.data.Dataset`に使えます。\n",
        "\n",
        "推論モードでデータの損失と評価指標を**評価**する例を示します： "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mhDbOHEK0E2Y",
        "colab": {}
      },
      "source": [
        "data = np.random.random((1000, 32))\n",
        "labels = random_one_hot_labels((1000, 10))\n",
        "\n",
        "model.evaluate(data, labels, batch_size=32)\n",
        "\n",
        "model.evaluate(dataset, steps=30)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UXUTmDfb0E2b"
      },
      "source": [
        "**推論** 結果を最終層のNumPy配列として出力する例を示します:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "9e3JsSoQ0E2c",
        "colab": {}
      },
      "source": [
        "result = model.predict(data, batch_size=32)\n",
        "print(result.shape)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "fzEOW4Cn0E2h"
      },
      "source": [
        "## 高度なモデルの構築\n",
        "\n",
        "### Functional API\n",
        "\n",
        "`tf.keras.Sequential` モデルは層を積み重ねる単純なつくりであり、あらゆるモデルに対応しているわけではありません。\n",
        "以下に挙げる複雑な構成のモデルを構築するには\n",
        "[Keras functional API](https://keras.io/getting-started/functional-api-guide/)\n",
        "を使いましょう：\n",
        "\n",
        "* 入力ヘッドが複数あるモデル\n",
        "* 出力ヘッドが複数あるモデル\n",
        "* 共有層（おなじ層が複数回呼び出される）を含むモデル\n",
        "* （残差結合のように）データの流れが分岐するモデル \n",
        "\n",
        "Functional API を用いたモデル構築の流れ：\n",
        "\n",
        "1. 層のインスタンスは呼び出し可能で、テンソルを返します。\n",
        "2. 入力テンソルと出力テンソルを使って`tf.keras.Model`インスタンスを定義します。\n",
        "3. モデルは`Sequential`モデルと同様の方法で学習します。\n",
        "\n",
        "Functional API を使って全結合ネットワークを構築する例を示します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mROj832r0E2i",
        "colab": {}
      },
      "source": [
        "inputs = tf.keras.Input(shape=(32,))  # プレイスホルダのテンソルを返します。\n",
        "\n",
        "# 層のインスタンスは呼び出し可能で、テンソルを返します。\n",
        "x = layers.Dense(64, activation='relu')(inputs)\n",
        "x = layers.Dense(64, activation='relu')(x)\n",
        "predictions = layers.Dense(10, activation='softmax')(x)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "AFmspHeG1_W7"
      },
      "source": [
        "inputsとoutputsを引数にモデルをインスタンス化します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5k5uzlyu16HM",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Model(inputs=inputs, outputs=predictions)\n",
        "\n",
        "# コンパイル時に学習方法を指定します。\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "# 5エポック学習します。\n",
        "model.fit(data, labels, batch_size=32, epochs=5)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "EcKSLH3i0E2k"
      },
      "source": [
        "### モデルの派生\n",
        "\n",
        "`tf.keras.Model` を継承し順伝播を定義することでカスタムモデルを構築できます。\n",
        "`__init__` メソッドにクラス インスタンスの属性として層をつくります。\n",
        "`call` メソッドに順伝播を定義します。 \n",
        "\n",
        "順伝播を命令型で記載できるため、モデルの派生は\n",
        "[Eagerモード](./eager.md) でより威力を発揮します。\n",
        "\n",
        "キーポイント：目的にあったAPIを選択しましょう。派生モデルは柔軟性を与えてくれますが、その代償にモデルはより複雑になりエラーを起こしやすくなります。目的がFunctional APIで賄えるのであれば、そちらを使いましょう。\n",
        "\n",
        "`tf.keras.Model`を継承して順伝播をカスタマイズした例を以下に示します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "KLiHWzcn2Fzk",
        "colab": {}
      },
      "source": [
        "class MyModel(tf.keras.Model):\n",
        "\n",
        "  def __init__(self, num_classes=10):\n",
        "    super(MyModel, self).__init__(name='my_model')\n",
        "    self.num_classes = num_classes\n",
        "    # 層をここに定義します。\n",
        "    self.dense_1 = layers.Dense(32, activation='relu')\n",
        "    self.dense_2 = layers.Dense(num_classes, activation='sigmoid')\n",
        "\n",
        "  def call(self, inputs):\n",
        "    # （`__init__`）にてあらかじめ定義した層を使って\n",
        "    # 順伝播をここに定義します。\n",
        "    x = self.dense_1(inputs)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "  def compute_output_shape(self, input_shape):\n",
        "    # 派生モデルを使用する場合、\n",
        "    # このメソッドをオーバーライドすることになります。\n",
        "    # 派生モデルを使用しない場合、このメソッドは省略可能です。\n",
        "    shape = tf.TensorShape(input_shape).as_list()\n",
        "    shape[-1] = self.num_classes\n",
        "    return tf.TensorShape(shape)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ShDD4fv72KGc"
      },
      "source": [
        "今定義した派生モデルをインスンス化します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "42C-qQHm0E2l",
        "colab": {}
      },
      "source": [
        "model = MyModel(num_classes=10)\n",
        "\n",
        "# コンパイル時に学習方法を指定します。\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "# 5エポック学習します。\n",
        "model.fit(data, labels, batch_size=32, epochs=5)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "yqRQiKj20E2o"
      },
      "source": [
        "### 層のカスタマイズ\n",
        "\n",
        "`tf.keras.layers.Layer`を継承して層をカスタマイズするには、以下のメソッドを実装します： \n",
        "\n",
        "* `build`： 層の重みを定義します。`add_weight`メソッドで重みを追加します。\n",
        "* `call`： 順伝播を定義します。\n",
        "* `compute_output_shape`: 入力の形状をもとに出力の形状を算出する方法を指定します。\n",
        "* 必須ではありませんが、`get_config`メソッド と `from_config` クラスメソッドを実装することで層をシリアライズすることができます。\n",
        "\n",
        "入力のカーネル行列を `matmul` （行列乗算）するカスタム層の実装例："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "l7BFnIHr2WNc",
        "colab": {}
      },
      "source": [
        "class MyLayer(layers.Layer):\n",
        "\n",
        "  def __init__(self, output_dim, **kwargs):\n",
        "    self.output_dim = output_dim\n",
        "    super(MyLayer, self).__init__(**kwargs)\n",
        "\n",
        "  def build(self, input_shape):\n",
        "    shape = tf.TensorShape((input_shape[1], self.output_dim))\n",
        "    # 学習可能な重みを指定します。\n",
        "    self.kernel = self.add_weight(name='kernel',\n",
        "                                  shape=shape,\n",
        "                                  initializer='uniform',\n",
        "                                  trainable=True)\n",
        "    # 最後に`build` メソッドを呼ぶのをお忘れなく。\n",
        "    super(MyLayer, self).build(input_shape)\n",
        "\n",
        "  def call(self, inputs):\n",
        "    return tf.matmul(inputs, self.kernel)\n",
        "\n",
        "  def compute_output_shape(self, input_shape):\n",
        "    shape = tf.TensorShape(input_shape).as_list()\n",
        "    shape[-1] = self.output_dim\n",
        "    return tf.TensorShape(shape)\n",
        "\n",
        "  def get_config(self):\n",
        "    base_config = super(MyLayer, self).get_config()\n",
        "    base_config['output_dim'] = self.output_dim\n",
        "    return base_config\n",
        "\n",
        "  @classmethod\n",
        "  def from_config(cls, config):\n",
        "    return cls(**config)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8wXDRgXV2ZrF"
      },
      "source": [
        "カスタマイズした層を使ってモデルを構築します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "uqH-cY0h0E2p",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([\n",
        "    MyLayer(10),\n",
        "    layers.Activation('softmax')])\n",
        "\n",
        "# コンパイル時に学習方法を指定します。\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "# 5エポック学習します。\n",
        "model.fit(data, labels, batch_size=32, epochs=5)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Lu8cc3AJ0E2v"
      },
      "source": [
        "## コールバック\n",
        "\n",
        "コールバックは、学習中のモデルの挙動をカスタマイズするためにモデルに渡されるオブジェクトです。\n",
        "コールバック関数は自作する、もしくは以下に示す`tf.keras.callbacks`が提供する組み込み関数を利用できます：\n",
        "\n",
        "* `tf.keras.callbacks.ModelCheckpoint`：モデルのチェックポイントを一定間隔で保存します。\n",
        "* `tf.keras.callbacks.LearningRateScheduler`：学習率を動的に変更します。\n",
        "* `tf.keras.callbacks.EarlyStopping`：評価パフォーマンスが向上しなくなったら学習を中断させます。\n",
        "* `tf.keras.callbacks.TensorBoard`： モデルの挙動を\n",
        "  [TensorBoard](./summaries_and_tensorboard.md)で監視します。\n",
        "\n",
        "`tf.keras.callbacks.Callback`を使用するには、モデルの `fit` メソッドにコールバック関数を渡します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "rdYwzSYV0E2v",
        "colab": {}
      },
      "source": [
        "callbacks = [\n",
        "  # `val_loss` が2エポック経っても改善しなければ学習を中断させます。\n",
        "  tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),\n",
        "  # TensorBoard用ログを`./logs` ディレクトリに書き込みます。\n",
        "  tf.keras.callbacks.TensorBoard(log_dir='./logs')\n",
        "]\n",
        "model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,\n",
        "          validation_data=(val_data, val_labels))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ghhaGfX62abv"
      },
      "source": [
        "<a id='weights_only'></a>\n",
        "## 保存と復元"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "qnl7K-aI0E2z"
      },
      "source": [
        "### 重みのみ\n",
        "\n",
        "`tf.keras.Model.save_weights`を使ってモデルの重みの保存やロードを行います。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "uQIANjB94fLB",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([\n",
        "layers.Dense(64, activation='relu', input_shape=(32,)),\n",
        "layers.Dense(10, activation='softmax')])\n",
        "\n",
        "model.compile(optimizer=tf.train.AdamOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "4eoHJ-ny0E21",
        "colab": {}
      },
      "source": [
        "# TensorFlow チェックポイント ファイルに重みを保存します。\n",
        "model.save_weights('./weights/my_model')\n",
        "\n",
        "# モデルの状態を復元します。\n",
        "# 復元対象のモデルと保存されていた重みのモデル構造が同一である必要があります。\n",
        "model.load_weights('./weights/my_model')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "u25Id3xe0E25"
      },
      "source": [
        "デフォルトでは、モデルの重みは\n",
        "[TensorFlow チェックポイント](./checkpoints.md) 形式で保存されます。\n",
        "重みはKerasのHDF5形式でも保存できます（マルチバックエンド実装のKerasではHDF5形式がデフォルト）："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "JSAYoFEd0E26",
        "colab": {}
      },
      "source": [
        "# 重みをHDF5形式で保存します。\n",
        "model.save_weights('my_model.h5', save_format='h5')\n",
        "\n",
        "# モデルの状態を復元します。\n",
        "model.load_weights('my_model.h5')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mje_yKL10E29"
      },
      "source": [
        "### 構成のみ\n",
        "\n",
        "モデルの構成も保存可能です。\n",
        "モデル構造を重み抜きでシリアライズします。\n",
        "元のモデルのコードがなくとも、保存された構成で再構築できます。\n",
        "Kerasがサポートしているシリアライズ形式は、JSONとYAMLです。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "EbET0oJTzGkq",
        "colab": {}
      },
      "source": [
        "# JSON形式にモデルをシリアライズします\n",
        "json_string = model.to_json()\n",
        "json_string"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "pX_badhH3yWV",
        "colab": {}
      },
      "source": [
        "import json\n",
        "import pprint\n",
        "pprint.pprint(json.loads(json_string))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Q7CIa05r4yTb"
      },
      "source": [
        "JSONから（新たに初期化して）モデルを再構築します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "J9UFv9k00E2_",
        "colab": {}
      },
      "source": [
        "fresh_model = tf.keras.models.model_from_json(json_string)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "t5NHtICh4uHK"
      },
      "source": [
        "YAML形式でモデルを保存するには、\n",
        "**TensorFlowをインポートする前に** あらかじめ`pyyaml`をインストールしておく必要があります："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "aj24KB3Z36S4",
        "colab": {}
      },
      "source": [
        "yaml_string = model.to_yaml()\n",
        "print(yaml_string)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "O53Kerfl43v7"
      },
      "source": [
        "YAMLからモデルを再構築します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "77yRuwg03_MG",
        "colab": {}
      },
      "source": [
        "fresh_model = tf.keras.models.model_from_yaml(yaml_string)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "xPvOSSzM0E3B"
      },
      "source": [
        "注意：`call`メソッド内ににPythonコードでモデル構造を定義するため、派生モデルはシリアライズできません。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "iu8qMwld4-71"
      },
      "source": [
        "\n",
        "### モデル全体\n",
        "\n",
        "モデルの重み、構成からオプティマイザ設定までモデル全体をファイルに保存できます。\n",
        "そうすることで、元のコードなしに、チェックポイントで保存したときと全く同じ状態から学習を再開できます。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "45oNY34Z0E3C",
        "colab": {}
      },
      "source": [
        "# 層の浅いモデルを構築します。\n",
        "model = tf.keras.Sequential([\n",
        "  layers.Dense(64, activation='relu', input_shape=(32,)),\n",
        "  layers.Dense(10, activation='softmax')\n",
        "])\n",
        "model.compile(optimizer='rmsprop',\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "model.fit(data, labels, batch_size=32, epochs=5)\n",
        "\n",
        "\n",
        "# HDF5ファイルにモデル全体を保存します。\n",
        "model.save('my_model.h5')\n",
        "\n",
        "# 重みとオプティマイザを含む 全く同一のモデルを再構築します。\n",
        "model = tf.keras.models.load_model('my_model.h5')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "PMOWhDOB0E3E"
      },
      "source": [
        "## Eagerモード\n",
        "\n",
        "[Eagerモード](./eager.md) は、オペレーションを即時に評価する命令型のプログラミング環境です。\n",
        "Kerasでは必要ありませんが、`tf.keras`でサポートされておりプログラムを検査しデバッグするのに便利です。\n",
        "\n",
        "すべての`tf.keras`モデル構築用APIは、Eagerモード互換性があります。\n",
        "`Sequential` や Functional APIも使用できますが、\n",
        "Eagerモードは特に**派生モデル** の構築や\n",
        "**層のカスタマイズ**に有益です。\n",
        "（既存の層の組み合わせでモデルを作成するAPIの代わりに）\n",
        "順伝播をコードで実装する必要があります。\n",
        "\n",
        "詳しくは [Eagerモード ガイド](./eager.md#build_a_model) \n",
        "（カスタマイズした学習ループと`tf.GradientTape`を使ったKerasモデルの適用事例）をご参照ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2wG3NVco5B5V"
      },
      "source": [
        "## 分散\n",
        "\n",
        "### Estimators\n",
        "\n",
        "[Estimators](./estimators.md) は分散学習を行うためのAPIです。\n",
        "実運用に耐えるモデルを巨大なデータセットを用いて分散学習するといった産業利用を目的にすえています。\n",
        "\n",
        "`tf.keras.Model`で`tf.estimator` APIによる学習を行うには、\n",
        "`tf.keras.estimator.model_to_estimator`を使ってKerasモデルを `tf.estimator.Estimator`オブジェクトに変換する必要があります。\n",
        "\n",
        "[KerasモデルからEstimatorsを作成する](./estimators.md#creating_estimators_from_keras_models)をご参照ください。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "cVg0vfTO0E3E",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([layers.Dense(64, activation='relu', input_shape=(32,)),\n",
        "                          layers.Dense(10,activation='softmax')])\n",
        "\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "estimator = tf.keras.estimator.model_to_estimator(model)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "S7FKvikO0E3H"
      },
      "source": [
        "注意：[Estimator input functions](./premade_estimators.md#create_input_functions)をデバッグしてデータの検査を行うには[Eagerモード](./eager.md)で実行してください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6PJZ6e9J5JHF"
      },
      "source": [
        "### マルチGPU\n",
        "\n",
        "`tf.keras`モデルは`tf.contrib.distribute.DistributionStrategy`を使用することでマルチGPU上で実行できます。\n",
        "このAPIを使えば、既存コードをほとんど改変することなく分散学習へ移行できます。\n",
        "\n",
        "目下、分散方式として`tf.contrib.distribute.MirroredStrategy`のみサポートしています。\n",
        "`MirroredStrategy` は、シングルマシン上でAllReduce を使った同期学習によりin-grapnレプリケーションを行います。\n",
        "Kerasで`DistributionStrategy`を使用する場合は、`tf.keras.estimator.model_to_estimator`を使って\n",
        "`tf.keras.Model` を`tf.estimator.Estimator`に変換し、Estimatorインスタンスを使って分散学習を行います。\n",
        "\n",
        "以下の例では、シングルマシンのマルチGPUに`tf.keras.Model`を分散します。\n",
        "\n",
        "まず、単純なモデルを定義します："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sbaRr7g-0E3I",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential()\n",
        "model.add(layers.Dense(16, activation='relu', input_shape=(10,)))\n",
        "model.add(layers.Dense(1, activation='sigmoid'))\n",
        "\n",
        "optimizer = tf.train.GradientDescentOptimizer(0.2)\n",
        "\n",
        "model.compile(loss='binary_crossentropy', optimizer=optimizer)\n",
        "model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "yw4hSJme0E3L"
      },
      "source": [
        "**入力パイプライン**を定義します。`input_fn` は、複数デバイスにデータを配置するのに使用する `tf.data.Dataset` を返します。\n",
        "各デバイスは、入力バッチの一部（デバイス間で均等に分割）を処理します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "CxJW1QMH0E3L",
        "colab": {}
      },
      "source": [
        "def input_fn():\n",
        "  x = np.random.random((1024, 10))\n",
        "  y = np.random.randint(2, size=(1024, 1))\n",
        "  x = tf.cast(x, tf.float32)\n",
        "  dataset = tf.data.Dataset.from_tensor_slices((x, y))\n",
        "  dataset = dataset.repeat(10)\n",
        "  dataset = dataset.batch(32)\n",
        "  return dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rO9MiL6X0E3O"
      },
      "source": [
        "次に、 `tf.estimator.RunConfig`を作成し、 `train_distribute` 引数に`tf.contrib.distribute.MirroredStrategy` インスタンスを設定します。`MirroredStrategy`を作成する際、デバイスの一覧を指定する、もしくは引数で`num_gpus`（GPU数）を設定することができます。デフォルトでは、使用可能なすべてのGPUを使用する設定になっています："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "BEwFq4PM0E3P",
        "colab": {}
      },
      "source": [
        "strategy = tf.contrib.distribute.MirroredStrategy()\n",
        "config = tf.estimator.RunConfig(train_distribute=strategy)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "TcnwYVun0E3R"
      },
      "source": [
        "Kerasモデルを `tf.estimator.Estimator` インスタンスへ変換します。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "VSvbuIID0E3S",
        "colab": {}
      },
      "source": [
        "keras_estimator = tf.keras.estimator.model_to_estimator(\n",
        "  keras_model=model,\n",
        "  config=config,\n",
        "  model_dir='/tmp/model_dir')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "N6BXU5F90E3U"
      },
      "source": [
        "最後に、`input_fn` と `steps`引数を指定して `Estimator` インスタンスを学習します： "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "XKoJ2wUH0E3U",
        "colab": {}
      },
      "source": [
        "keras_estimator.train(input_fn=input_fn, steps=10)"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}